The macro commands GetRow, GetColumn, PutRow and PutColumn can be used for accessing the image on a line by line basis. These macro routines use what is know as the LineBuffer array. This array is of the internally defined type known as LineType. Pascal routines such as GetLine use the LineType. If you plan on accessing 'lines' of the image within your macro, it would might be worth your while to examine the pascal examples in the pascal section. After looking at these, you probably will see how to use the LineBuffer array in a macro.
First look at the definition of LineType. LineType is globally declared as:
LineType = packed array [0..MaxLine] of UnsignedByte;
Naturally, UnsignedByte has been type defined as:
UnsignedByte = 0..255;
The example below is a macro which uses the linebuffer array. If you are interested in using a macro to get at image data, this example should be fairly clear.
Macro 'Invert lines of image';
var
i,j,width,height:integer;
begin
GetPicSize(width,height);
for i:=1 to height do begin
GetRow(0,i,width);
for j:=1 to width do begin
LineBuffer[j] := 255-LineBuffer[j];
end;
PutRow(0,i,width);
end;
One simple way to load data from disk is to create a window and dump information to it. An example of this is a macro which imports files created by the IPLab program. The macro reads the first 100 bytes from the file into a temporary window. It erases the window when it is through finding useful header information.
macro 'Import IPLab File';
var
width,height,offset:integer;
begin
width:=100;
height:=1;
offset:=0;
SetImport('8-bit');
SetCustom(width,height,offset);
Import(''); {Read in header as an image, prompting for file name.}
width := (GetPixel(8,0)*256) + GetPixel(9,0);
height := (GetPixel(12,0)*256) + GetPixel(13,0);
Dispose;
offset:=2120; {The IPLab offset}
SetImport('16-bit Signed; Calibrate; Autoscale');
SetCustom(width,height,offset);
Import(''); {No prompt this time; Import remembers the name.}
end;
See the pascal section for examples of reading from disk (non-image data) to User arrays.
From wayne@helix.nih.gov (Wayne Rasband) reply on nih-image@soils.umn.edu
>is there a possiblility to define 'open' access to the file contents of a
>folder (with, lets say, Images of 2.5 MB size each)? I want to do a Batch list
>for Background subtraction and contrast enhancement.
It's easy to write a macro to process a series of images in a folder as long as the file names contain a numerical sequence such as 'file01.pic', 'file02.pic', 'file03.pic', etc. I have included an example macro that does this.
macro 'Batch Processing Example';
{
Reads from disk and processes a set of images too large to
simultaneously fit in memory. The image names names must be
in the form 'image001', 'image002', ..., but this can be changed.
}
var
i:integer;
begin
for i:=1 to 1000 do begin
open('image',i:3);
{process;}
save;
close;
end;
end;
From wayne@helix.nih.gov (Wayne Rasband) reply on nih-image@soils.umn.edu
You should be able to process many files and only have to see one dialog box. For example, only one dialog appears when you run the following macro as long as 'A', 'B' and 'C' are in the same folder.
macro 'test';
begin
Open('A');
Invert;
Save;
Close;
Open('B');
Another way to avoid the dialog box is to use full directory paths as in the following example.
macro 'test';
begin
Open('hd400:images:A');
Invert;
Save;
Close;
Open('hd400:images:B');
Invert;
Save;
Close;
Open('hd400:images:C');
Invert;
Save;
Close;
end;
In V1.55, you can use a full folder path [.e.g., SaveAs('HD400:My Images:mage001')] and the dialog box will not be displayed.
From wayne@helix.nih.gov (Wayne Rasband) reply on nih-image@soils.umn.edu
According to "Inside Macintosh", ticks are counted at the rate of 60 per second. You can varify this by running the enclosed macro and timing the interval between beeps.
macro 'TickCount Test';
{"Beeps" every 10 seconds}
var
interval,ticks:integer;
begin
interval:=600;
ticks:=TickCount+interval;
repeat
if TickCount>=ticks then begin
beep;
ticks:=ticks+interval;
end;
until button;
end;
You can modify the way an image appears by altering the RedLUT, GreenLUT and BlueLUT. This is simple and straightforward enough. You can access the RedLUT, GreenLUT and BlueLUT arrays from both macros and from Pascal.
The pascal definitions are:
LutArray = packed array[0..255] of byte;
RedLUT, GreenLUT, BlueLUT: LutArray;
Here is an example macro which finds any gray or black components in a color image and sets them to white. It's useful for seperating certain kinds of medical data.
macro 'Remove Equal RGB [V]';
{Changes only the LUT, removes gray component from an image}
var
i,Value:integer;
begin
for i:=1 to 254 do begin
If ((RedLUT[i] = BlueLUT[i]) and (RedLUT[i] = GreenLUT[i]))
then begin
RedLut[i] :=255;
BlueLut[i] := 255;
GreenLut[i] :=255;
end;
end;
ChangeValues(255,255,0); {remove black}
UpdateLUT;
end;
From wayne@helix.nih.gov (Wayne Rasband) reply on nih-image@soils.umn.edu
>>Here is a macro that writes the current date and time to a text window.
>
>Can this macro be modified to write the date and time into the "Show Results"
>window?
No, but it can be modified to also store results into the text window. I have included a macro that does that. Here is what the output from this macro looks like:
Date=94:5:31
Time=14:45:24
Area=10000.000
Mean=80.198
macro 'Write Results to Text Window';
var
year,month,day,hour,minute,second,dow:integer;
begin
GetTime(year,month,day,hour,minute,second,dow);
Measure;
NewTextWindow('My Results');
writeln('Date=',year-1900:1,':',month:1,':',day:1);
writeln('Time=',hour:1,':'minute:1,':',second:1);
writeln('Area=',rArea[rCount]:1:3);
writeln('Mean=',rMean[rCount]:1:3);
end;
From reply of jy@nhm.ic.ac.uk on nih-image@soils.umn.edu
>Does anyone know of an easy way to get the actual points in x,y coordinates and
>the values at each point from the profile plot data using macros?
Image 1.54 introduced a new command to +/- allow this:
"A command was added to the macro language for making profile plot data available to macro routines. It has the form "GetPlotData(count,ppv,min,max)", where count is the number of values, ppv is the number of pixels averaged for each value, and min and max are the minimum and maximum values. The plot data values are returned in a built-in real array named PlotData, which uses indexes in the range 0-4095. The macro "Plot Profile" in "Plotting Macros" illustrates how to use GetPlotData and PlotData." [from the changes file]
To help answer your question further....
1. For a count value of n the PlotData array will have meaningful values from 0 to n-1 (higher array values are accessible but will contain old/meaningless results).
2. Count is equal to the line length, in pixels, rounded to the nearest integer value. But...
3. Substantially more pixels are usually highlighted by a line selection, and this seems to have only an approximate corelation with the pixels used by PlotData.
4 The PlotData array contains real-numbers (not integers) which presumably are derived from a weighted average of pixels rather than being the values of single pixels - even when ppv is 1. Because of this it is not possible to relate PlotData values to single locations.
5. My conclusion after some experimentation is that;
after GetLine(x1,y1,x2,y2,lw);
and GetPlotData(count,ppv,min,max);
The following function will probably return the centre of the location used to derive PlotData[c]:
ypos:=y1+(c+0.5)/(count)*(y2-y1);
xpos:=x1+(c+0.5)/(count)*(x2-x1);
Image allows you to call by name user developed pascal routines from a macro which you write. Outlined below are example steps you can take to achieve this. You can pass into your pascal procedure up to three extended values. If you don't have any values to pass than pass a zero or any other value.
Step 1:
Write a macro or macro procedure which calls UserCode(n,p1,p2,p3). Be sure to pass values for n, p1, p2 and p3. The example below will call a routine in User.p to add and display two numbers. Note that n equals 1 in this call, because the routine calls the 1st UserMacroCode. This is further explained in step 3.
macro 'Add two values';
var
NoValue:integer;
ValueOne,ValueTwo:Real;
begin
NoValue := 0;
ValueOne := 2.0;
ValueTwo := 3.14;
UserCode('AddTwoNumbers',ValueOne,ValueTwo,NoValue);
end;
Step 2:
Write a pascal routine in the User.p module. Again, this example simply adds two numbers and shows the result in the Info Window.
procedure
AddTwoNumbers (Value1, Value2: extended);
var
str1, str2, str3: str255;
Result: extended;
begin
Result := Value1 + Value2;
RealToString(Value1, 5, 2, str1);
RealToString(Value2, 5, 2, str2);
RealToString(Result, 5, 2, str3);
ShowMessage(Concat('1st number = ', str1, cr, '2nd number = ', str2, cr, 'Added result = ', str3));
end;
Step 3:
Modify the UserMacroCode procedure to call your pascal procedure. The UserMacroCode procedure is found at the bottom of the User.p module. Because you could call differing UserCode routines, the string you pass into UserCode selects which routine you would like to call. This example checks to see if you have passed the string 'AddTwoNumbers'.
procedure UserMacroCode (str: str255; Param1, Param2, Param3: extended);
begin
MakeLowerCase(str);
if pos('addtwonumbers', str) <> 0 then begin
AddTwoNumbers(Param1, Param2);
exit(UserMacroCode);
end;
ShowNoCodeMessage;
end;
Step 4:
Compile your modified version of Image. Load your macro and execute away. Shown below is the result of the entire example.
